﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mahanet.Abyss.PathCalculation
{
	public class Coordinate2DDataGenerator : IDataGenerator<double, Coordinate2D>
	{
		#region Consts

		public static double APPROXIMATE_GRAVITY { get; private set; }

		#endregion

		#region Events

		public event DataGenerationEvent<double, Coordinate2D> DataGenerated;

		#endregion

		#region Fields

		private static Random m_Random = new Random();
	
		#endregion

		#region C'tor

		public Coordinate2DDataGenerator()
		{
			LastDataSeriesId = 0;
		}

		#endregion

		#region Public Properties

		public int LastDataSeriesId { get; private set; }
		public FunctionApproximation<double, Coordinate2D> GeneratedFunction { get; private set; }

		#endregion

		#region Public Methods

		public void GenerateFunction(double maxX, double maxY, double maxT)
		{
			LastDataSeriesId++;

			APPROXIMATE_GRAVITY = -4 * 0.8 * maxY / (maxT * maxT);

			double ay = APPROXIMATE_GRAVITY * (0.97 + 0.06 * m_Random.NextDouble());

			double xStartPoint = 0.1 * maxX * m_Random.NextDouble();
			double xEndPoint = maxX * (0.9 + 0.1 * m_Random.NextDouble());
			//double yHighPoint = maxY * (0.6 + 0.4 * m_Random.NextDouble());
			double yHighPoint = -ay * maxT * maxT / 4;

			//double ay = -4 * yHighPoint / (maxT * maxT);
			double by = 4 * yHighPoint / maxT;
			double cy = 0;

			double ax = (xEndPoint - xStartPoint) / maxT;
			double bx = xStartPoint;

			FunctionApproximation<double, double> yFunction = (t) => { return ay * t * t + by * t + cy; };
			FunctionApproximation<double, double> xFunction = (t) => { return ax * t + xStartPoint; };

			GeneratedFunction = (t) => new Coordinate2D(xFunction(t), yFunction(t));
		}

		public void GenerateDataPoints(IEnumerable<double> sourcePoints, double maxErrorPercentage)
		{
			foreach (double point in sourcePoints)
			{
				GenerateDataPoint(point, maxErrorPercentage);
			}
		}

		public void GenerateDataPoint(double point, double maxErrorPercentage)
		{
			Coordinate2D result = GeneratedFunction(point);
			double xValue = result.X;
			double yValue = result.Y;

			xValue *= 1 + (m_Random.NextDouble() - 0.5) * maxErrorPercentage;
			yValue *= 1 + (m_Random.NextDouble() - 0.5) * maxErrorPercentage;

			RaiseDataGeneratedEvent(new DataPoint<double, Coordinate2D>(point, new Coordinate2D(xValue, yValue)));
		}

		#endregion

		#region Private Methods

		private void RaiseDataGeneratedEvent(DataPoint<double, Coordinate2D> dataPoint)
		{
			if (this.DataGenerated != null)
			{
				this.DataGenerated(dataPoint, LastDataSeriesId);
			}
		}

		#endregion
	}
}
